/**
* Copyright (c) By zengqh.
*
* This program is just for fun or demo, in the hope that it  
* will be useful, you can redistribute it and/or modify freely.
*
* Time: 2012/07/16
* File: InputSystem.cpp
* Blog: http://www.cnblogs.com/zengqh/
**/

/*
 * to make it simple, i just use ois to handle the input system.
 * i copy it from the demo i write before, hehe.
 */

#include "OISInputManager.h"
#include "OISException.h"
#include "OISKeyboard.h"
#include "OISMouse.h"
#include "OISJoyStick.h"
#include "OISEvents.h"
#include "OISMultiTouch.h"

#include "../RenderWindow/RenderWindow.h"
#include "InputSystem.h"
#include <sstream>

template <> InputSystem* Singleton<InputSystem>::_singleton = NULL;

InputSystem::InputSystem( void ) :
mMouse( 0 ),
mKeyboard( 0 ),
mInputSystem( 0 ) {
}

InputSystem::~InputSystem( void ) {
	if ( mInputSystem ) {
		if ( mMouse ) {
			mInputSystem->destroyInputObject( mMouse );
			mMouse = 0;
		}

		if ( mKeyboard ) {
			mInputSystem->destroyInputObject( mKeyboard );
			mKeyboard = 0;
		}

		if ( mJoysticks.size() > 0 ) {
			itJoystick    = mJoysticks.begin();
			itJoystickEnd = mJoysticks.end();
			for(; itJoystick != itJoystickEnd; ++itJoystick ) {
				mInputSystem->destroyInputObject( *itJoystick );
			}

			mJoysticks.clear();
		}

		// If you use OIS1.0RC1 or above, uncomment this line
		// and comment the line below it
		mInputSystem->destroyInputSystem( mInputSystem );
		//mInputSystem->destroyInputSystem();
		mInputSystem = 0;

		// Clear Listeners
		mKeyListeners.clear();
		mMouseListeners.clear();
		mJoystickListeners.clear();
	}
}

InputSystem* InputSystem::createInputSystem()
{
	_singleton = new InputSystem;
	return _singleton;
}

void InputSystem::deleteInputSystem()
{
	safe_delete(_singleton);
}

void InputSystem::initialise( RenderWindow* render_window ) {
	if ( !mInputSystem ) {
		// Setup basic variables
		//OIS::ParamList paramList;    

		std::multimap<std::string, std::string> paramList;


		size_t windowHnd = render_window->getWidnowID();
		std::ostringstream windowHndStr;

		// Fill parameter list
		windowHndStr << (unsigned int) windowHnd;
		paramList.insert( std::make_pair( std::string( "WINDOW" ), windowHndStr.str() ) );

		mInputSystem = OIS::InputManager::createInputSystem(paramList); 

		// If possible create a buffered keyboard
		if (mInputSystem->getNumberOfDevices(OIS::OISKeyboard) > 0)
		{
			mKeyboard = (OIS::Keyboard*)( mInputSystem->createInputObject( OIS::OISKeyboard, true ) );
			mKeyboard->setEventCallback( this );
		}

		// If possible create a buffered mouse
		if (mInputSystem->getNumberOfDevices(OIS::OISMouse) > 0)
		{
			mMouse = (OIS::Mouse*)( mInputSystem->createInputObject( OIS::OISMouse, true ) );
			mMouse->setEventCallback( this );

			// Get window size
			unsigned int width, height;

			width = render_window->getWidth();
			height = render_window->getHeight();

			// Set mouse region
			this->setWindowExtents( width, height );
		}

		// If possible create all joysticks in buffered mode
		if (mInputSystem->getNumberOfDevices(OIS::OISJoyStick) > 0)
		{
			mJoysticks.resize( mInputSystem->getNumberOfDevices(OIS::OISJoyStick) );

			itJoystick    = mJoysticks.begin();
			itJoystickEnd = mJoysticks.end();
			for(; itJoystick != itJoystickEnd; ++itJoystick ) {
				(*itJoystick) = (OIS::JoyStick*)mInputSystem->createInputObject( OIS::OISJoyStick, true );
				(*itJoystick)->setEventCallback( this );
			}
		}
	}
}

void InputSystem::capture( void ) {
	// Need to capture / update each device every frame
	if ( mMouse ) {
		mMouse->capture();
	}

	if ( mKeyboard ) {
		mKeyboard->capture();
	}

	if ( mJoysticks.size() > 0 ) {
		itJoystick    = mJoysticks.begin();
		itJoystickEnd = mJoysticks.end();
		for(; itJoystick != itJoystickEnd; ++itJoystick ) {
			(*itJoystick)->capture();
		}
	}
}

void InputSystem::addKeyListener( OIS::KeyListener *keyListener, const std::string& instanceName ) {
	if ( mKeyboard ) {
		// Check for duplicate items
		itKeyListener = mKeyListeners.find( instanceName );
		if ( itKeyListener == mKeyListeners.end() ) {
			mKeyListeners[ instanceName ] = keyListener;
		}
		else {
			// Duplicate Item
		}
	}
}

void InputSystem::addMouseListener( OIS::MouseListener *mouseListener, const std::string& instanceName ) {
	if ( mMouse ) {
		// Check for duplicate items
		itMouseListener = mMouseListeners.find( instanceName );
		if ( itMouseListener == mMouseListeners.end() ) {
			mMouseListeners[ instanceName ] = mouseListener;
		}
		else {
			// Duplicate Item
		}
	}
}

void InputSystem::addJoystickListener( OIS::JoyStickListener *joystickListener, const std::string& instanceName ) {
	if ( mJoysticks.size() > 0 ) {
		// Check for duplicate items
		itJoystickListener = mJoystickListeners.find( instanceName );
		if ( itJoystickListener == mJoystickListeners.end() ) {
			mJoystickListeners[ instanceName ] = joystickListener;
		}
		else {
			// Duplicate Item
		}
	}
}

void InputSystem::removeKeyListener( const std::string& instanceName ) {
	// Check if item exists
	itKeyListener = mKeyListeners.find( instanceName );
	if ( itKeyListener != mKeyListeners.end() ) {
		mKeyListeners.erase( itKeyListener );
	}
	else {
		// Doesn't Exist
	}
}

void InputSystem::removeMouseListener( const std::string& instanceName ) {
	// Check if item exists
	itMouseListener = mMouseListeners.find( instanceName );
	if ( itMouseListener != mMouseListeners.end() ) {
		mMouseListeners.erase( itMouseListener );
	}
	else {
		// Doesn't Exist
	}
}

void InputSystem::removeJoystickListener( const std::string& instanceName ) {
	// Check if item exists
	itJoystickListener = mJoystickListeners.find( instanceName );
	if ( itJoystickListener != mJoystickListeners.end() ) {
		mJoystickListeners.erase( itJoystickListener );
	}
	else {
		// Doesn't Exist
	}
}

void InputSystem::removeKeyListener( OIS::KeyListener *keyListener ) {
	itKeyListener    = mKeyListeners.begin();
	itKeyListenerEnd = mKeyListeners.end();
	for(; itKeyListener != itKeyListenerEnd; ++itKeyListener ) {
		if ( itKeyListener->second == keyListener ) {
			mKeyListeners.erase( itKeyListener );
			break;
		}
	}
}

void InputSystem::removeMouseListener( OIS::MouseListener *mouseListener ) {
	itMouseListener    = mMouseListeners.begin();
	itMouseListenerEnd = mMouseListeners.end();
	for(; itMouseListener != itMouseListenerEnd; ++itMouseListener ) {
		if ( itMouseListener->second == mouseListener ) {
			mMouseListeners.erase( itMouseListener );
			break;
		}
	}
}

void InputSystem::removeJoystickListener( OIS::JoyStickListener *joystickListener ) {
	itJoystickListener    = mJoystickListeners.begin();
	itJoystickListenerEnd = mJoystickListeners.end();
	for(; itJoystickListener != itJoystickListenerEnd; ++itJoystickListener ) {
		if ( itJoystickListener->second == joystickListener ) {
			mJoystickListeners.erase( itJoystickListener );
			break;
		}
	}
}

void InputSystem::removeAllListeners( void ) {
	mKeyListeners.clear();
	mMouseListeners.clear();
	mJoystickListeners.clear();
}

void InputSystem::removeAllKeyListeners( void ) {
	mKeyListeners.clear();
}

void InputSystem::removeAllMouseListeners( void ) {
	mMouseListeners.clear();
}

void InputSystem::removeAllJoystickListeners( void ) {
	mJoystickListeners.clear();
}

void InputSystem::setWindowExtents( int width, int height ) {
	// Set mouse region (if window resizes, we should alter this to reflect as well)
	const OIS::MouseState &mouseState = mMouse->getMouseState();
	mouseState.width  = width;
	mouseState.height = height;
}

OIS::Mouse* InputSystem::getMouse( void ) {
	return mMouse;
}

OIS::Keyboard* InputSystem::getKeyboard( void ) {
	return mKeyboard;
}

OIS::JoyStick* InputSystem::getJoystick( unsigned int index ) {
	// Make sure it's a valid index
	if ( index < mJoysticks.size() ) {
		return mJoysticks[ index ];
	}

	return 0;
}

int InputSystem::getNumOfJoysticks( void ) {
	// Cast to keep compiler happy ^^
	return (int) mJoysticks.size();
}

bool InputSystem::keyPressed( const OIS::KeyEvent &e ) {

	if (e.key == OIS::KC_ESCAPE)
	{
		exit(0);
	}

	itKeyListener    = mKeyListeners.begin();
	itKeyListenerEnd = mKeyListeners.end();
	for(; itKeyListener != itKeyListenerEnd; ++itKeyListener ) {
		if (!itKeyListener->second->keyPressed( e ))
			break;
	}

	return true;
}

bool InputSystem::keyReleased( const OIS::KeyEvent &e ) {
	itKeyListener    = mKeyListeners.begin();
	itKeyListenerEnd = mKeyListeners.end();
	for(; itKeyListener != itKeyListenerEnd; ++itKeyListener ) {
		if (!itKeyListener->second->keyReleased( e ))
			break;
	}

	return true;
}

bool InputSystem::mouseMoved( const OIS::MouseEvent &e ) {
	itMouseListener    = mMouseListeners.begin();
	itMouseListenerEnd = mMouseListeners.end();
	for(; itMouseListener != itMouseListenerEnd; ++itMouseListener ) {
		if (!itMouseListener->second->mouseMoved( e ))
			break;
	}

	return true;
}

bool InputSystem::mousePressed( const OIS::MouseEvent &e, OIS::MouseButtonID id ) {
	itMouseListener    = mMouseListeners.begin();
	itMouseListenerEnd = mMouseListeners.end();
	for(; itMouseListener != itMouseListenerEnd; ++itMouseListener ) {
		if (!itMouseListener->second->mousePressed( e, id ))
			break;
	}

	return true;
}

bool InputSystem::mouseReleased( const OIS::MouseEvent &e, OIS::MouseButtonID id ) {
	itMouseListener    = mMouseListeners.begin();
	itMouseListenerEnd = mMouseListeners.end();
	for(; itMouseListener != itMouseListenerEnd; ++itMouseListener ) {
		if (!itMouseListener->second->mouseReleased( e, id ))
			break;
	}

	return true;
}

bool InputSystem::povMoved( const OIS::JoyStickEvent &e, int pov ) {
	itJoystickListener    = mJoystickListeners.begin();
	itJoystickListenerEnd = mJoystickListeners.end();
	for(; itJoystickListener != itJoystickListenerEnd; ++itJoystickListener ) {
		if (!itJoystickListener->second->povMoved( e, pov ))
			break;
	}

	return true;
}

bool InputSystem::axisMoved( const OIS::JoyStickEvent &e, int axis ) {
	itJoystickListener    = mJoystickListeners.begin();
	itJoystickListenerEnd = mJoystickListeners.end();
	for(; itJoystickListener != itJoystickListenerEnd; ++itJoystickListener ) {
		if (!itJoystickListener->second->axisMoved( e, axis ))
			break;
	}

	return true;
}

bool InputSystem::sliderMoved( const OIS::JoyStickEvent &e, int sliderID ) {
	itJoystickListener    = mJoystickListeners.begin();
	itJoystickListenerEnd = mJoystickListeners.end();
	for(; itJoystickListener != itJoystickListenerEnd; ++itJoystickListener ) {
		if (!itJoystickListener->second->sliderMoved( e, sliderID ))
			break;
	}

	return true;
}

bool InputSystem::buttonPressed( const OIS::JoyStickEvent &e, int button ) {
	itJoystickListener    = mJoystickListeners.begin();
	itJoystickListenerEnd = mJoystickListeners.end();
	for(; itJoystickListener != itJoystickListenerEnd; ++itJoystickListener ) {
		if (!itJoystickListener->second->buttonPressed( e, button ))
			break;
	}

	return true;
}

bool InputSystem::buttonReleased( const OIS::JoyStickEvent &e, int button ) {
	itJoystickListener    = mJoystickListeners.begin();
	itJoystickListenerEnd = mJoystickListeners.end();
	for(; itJoystickListener != itJoystickListenerEnd; ++itJoystickListener ) {
		if (!itJoystickListener->second->buttonReleased( e, button ))
			break;
	}

	return true;
}